feat(haproxy): render empty-state when no HAProxy-capable boxes - #118
Merged
sarg3nt merged 1 commit intoMay 17, 2026
Merged
Conversation
…oxy boxes (#112) Previously, OverviewPage redirected to /settings/boxes whenever its server list was empty. After the haproxy-capability filter from the parent commit, that list can be empty for two distinct reasons: 1. No enabled boxes at all — operator hasn't configured anything. 2. Enabled boxes exist, but none advertise the haproxy gear (e.g. a TrueNAS host running gearbox-agent in a container without HAProxy installed). Redirecting on case 2 is hostile UX: the operator is on /haproxy on purpose and gets bounced away with no explanation. Render an inline InfoAlert instead, pointing them at the Bx fleet view and the boxes settings page so they can either fix the agent or add a different host. Touches the Overview templ to accept an optional emptyReason string; existing call site is the only one and is updated in this commit. StatusGridPage gets the same redirect-vs-render treatment (its empty-state markup is the template's own concern). Phase 2 slice of #112. Stacked on fix/issue-112-haproxy-tile-capabilities (PR #115). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
sarg3nt
merged commit May 17, 2026
ad83636
into
fix/issue-112-haproxy-tile-capabilities
3 checks passed
sarg3nt
added a commit
that referenced
this pull request
May 17, 2026
* feat(haproxy): filter dashboard tiles by agent haproxy capability (#112) The HAProxy dashboard at /haproxy iterates every enabled server and attaches `hx-get="/htmx/{box}/stats"` + `/metrics` to each row. On a box whose agent has no haproxy gear (e.g. container-mode agent on TrueNAS — see issue #112), those polls return 503 forever and the tiles stay empty. Light-hugger's deployment exhibits this whenever mjolnir is in the box roster. Plumb the framework Handler's CapabilitiesCache through into the ServerAdapter (wired in main.go via the new SetCapabilitiesCache / CapabilitiesCache pair) and add GetEnabledServersWithGearAvailable on the adapter. The HAProxy gear's overview + status-grid handlers now filter through that, so boxes whose probe table marks haproxy as unavailable just don't appear on the page. Fail-open contract: an agent whose capabilities can't be fetched (unreachable, no API key, TLS verify off) is still included, matching how filterGearsByAgentCapabilities handles the same case. A transient agent outage doesn't make the page go dark. Default landing route per box (the other half of slice 3 in the roadmap) is left for a follow-up — this PR is scoped to the tile- filtering symptom. Phase 2 slice of #112. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(haproxy): address Copilot review — parallelize fetches, correct gear list (#112) Two Copilot review findings on the original capability-aware filter: 1. Doc inaccuracy: the example agent-gear list included "services", but the agent has no `services` gear (the /api/v1/services endpoint is served by the `metrics` gear). Replace with the correct list and call out that some dashboard gears (services, alerts, bx) don't have agent counterparts and shouldn't be filtered via this helper. 2. O(N * fetchTimeout) cold-cache latency: synchronous fetches could stall a page render to N × fetchTimeout when multiple agents are unreachable. Parallelize across boxes with one goroutine per box and a sync.WaitGroup join, so cold-cache renders pay one round of timeout at most. Warm-cache renders hit the cache's read-lock and stay sub-millisecond. Box order is preserved by writing into a positional `keep []bool` and compacting after the wait. `go test -race` clean on services/handler/agent/haproxy packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(haproxy): render empty-state instead of redirecting when no HAProxy boxes (#112) (#118) Previously, OverviewPage redirected to /settings/boxes whenever its server list was empty. After the haproxy-capability filter from the parent commit, that list can be empty for two distinct reasons: 1. No enabled boxes at all — operator hasn't configured anything. 2. Enabled boxes exist, but none advertise the haproxy gear (e.g. a TrueNAS host running gearbox-agent in a container without HAProxy installed). Redirecting on case 2 is hostile UX: the operator is on /haproxy on purpose and gets bounced away with no explanation. Render an inline InfoAlert instead, pointing them at the Bx fleet view and the boxes settings page so they can either fix the agent or add a different host. Touches the Overview templ to accept an optional emptyReason string; existing call site is the only one and is updated in this commit. StatusGridPage gets the same redirect-vs-render treatment (its empty-state markup is the template's own concern). Phase 2 slice of #112. Stacked on fix/issue-112-haproxy-tile-capabilities (PR #115). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
sarg3nt
added a commit
that referenced
this pull request
May 17, 2026
…#112) (#133) RootRedirect's fallback chain hard-coded /haproxy as the dashboard default for any installation with at least one enabled box. On a TrueNAS-style deployment whose agent runs in a distroless container without HAProxy (Mjolnir), users would log in and be dropped on an empty /haproxy page that 503-storms on the per-box stat tiles. Make the landing route per-box and capability-aware: - /haproxy when the active box's agent reports haproxy available (preserves the historical landing for HAProxy-fronted deployments) - /metrics when haproxy isn't available but metrics is — the next-most-useful single-pane-of-glass for a host-only agent - /bx as the universal fallback so an active box with no advertised gears still has a place to land Fail-open to /haproxy when capabilities can't be fetched (agent down, no API key, older agent that pre-dates probing) so a transient outage doesn't change the dashboard's behavior. Per-user / system landing-path overrides (login.go::resolvePostLoginPath) keep priority over this — a user who has explicitly set their landing URL still gets it. Four tests exercise the new helper against a real httptest.Server returning capability responses: haproxy-prefers-haproxy, no-haproxy-falls-to-metrics, host-only-falls-to-bx, and agent-down-fails-open-to-haproxy. Phase 2 slice of #112. Stacked on fix/issue-112-haproxy-empty-state (#118). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/settings/boxes. Case B is hostile UX — the operator is on/haproxyon purpose and gets bounced away with no explanation. This PR renders an inlineInfoAlertfor case B pointing at the Bx fleet view and the boxes settings, and keeps the redirect for case A.pages.Overviewto accept an optionalemptyReasonstring; updates the single call site.StatusGridPagegets the same redirect-vs-render treatment.Phase 2 slice of #112. Stacked on #115 — review the parent first; merge order is #115 → this PR.
Test plan
go build ./...,go vet ./...,go test -count=1 ./internal/framework/handler/... ./internal/gears/haproxy/...clean/haproxyand confirm it still redirects to/settings/boxes(case A, unchanged)./haproxyand confirm it renders with the new InfoAlert instead of redirecting./haproxyand confirm the per-box tiles render normally (unchanged behavior).🤖 Generated with Claude Code